﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Web;

namespace PTop.Code.Excel
{
    /// <summary>
    /// 将实体模型导出到Excel
    /// 用法：
    /// new ExcelExportHelper<HViewDetailStatDto>(list.HViewDetailStat)
    /// .AddProperty(m => m.City, "城市")
    /// .AddProperty(m => m.Houseid, "房源id")
    /// .AddProperty(m => m.Agentid, "经纪人id")
    /// .AddProperty(m => m.GrabDate, "抢拍日期")
    /// .ToExcel("抢拍明细报表");
    /// </summary>
    /// <typeparam name="TModel"></typeparam>
    public class ExcelExportHelper<TModel>
    {
        #region 私有函数

        private readonly IEnumerable<TModel> _models;

        private readonly Dictionary<string, string> _dictionary;
        private readonly Dictionary<string, Func<object, string>> _funcDictionary;

        private readonly PropertyInfo[] _propertyInfos;

        #endregion

        #region 构造函数

        public ExcelExportHelper(IEnumerable<TModel> models)
        {
            _models = models;
            _dictionary = new Dictionary<string, string>();
            _funcDictionary = new Dictionary<string, Func<object, string>>();
            _propertyInfos = ObjectReflectionHelper.GetProperties<TModel>();
        }

        #endregion

        /// <summary>
        /// 增加导出Excel的属性
        /// </summary>
        /// <typeparam name="TModel"></typeparam>
        /// <param name="expression"></param>
        /// <param name="displayName"></param>
        /// <param name="func">值转换方法</param>
        public ExcelExportHelper<TModel> AddProperty(Expression<Func<TModel, object>> expression, string displayName = null, Func<object, string> func = null)
        {
            var pname = GetPropertyName(expression);
            var dname = displayName;
            if (string.IsNullOrEmpty(displayName))
            {
                dname = expression.Name;
            }
            _dictionary.Add(pname, dname);
            if (func != null)
            {
                _funcDictionary.Add(pname, func);
            }
            return this;
        }

        /// <summary>
        /// 输出Excel报表
        /// </summary>
        /// <param name="fileName">输出文件名</param>
        public void ToExcel(string fileName)
        {
            //判断绑定字段
            if (_dictionary == null || _dictionary.Count == 0)
            {
                throw new ArgumentNullException("绑定字段不能为空");
            }

            HttpContext.Current.Response.Clear();
            HttpContext.Current.Response.Charset = "GB2312";
            HttpContext.Current.Response.ContentEncoding = Encoding.GetEncoding("GB2312");
            HttpContext.Current.Response.ContentType = "application/ms-excel";
            if (!fileName.EndsWith(".xls"))
                fileName += ".xls";
            HttpContext.Current.Response.HeaderEncoding = Encoding.GetEncoding("gb2312");
            if (HttpContext.Current.Request.UserAgent.ToLower().IndexOf("firefox") > -1)
            {
                HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=\"" + fileName);
            }
            else
            {
                HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + fileName);
            }
            //输出Excel表头
            foreach (string dname in _dictionary.Values)
            {
                HttpContext.Current.Response.Write(dname + "\t");
            }
            HttpContext.Current.Response.Write("\n");
            //输出Excel内容
            if (_models != null)
            {
                foreach (var model in _models)
                {
                    foreach (string pname in _dictionary.Keys)
                    {
                        var pi = _propertyInfos.First(m => m.Name == pname);
                        var pv = pi.GetValue(model, null);
                        if (_funcDictionary.ContainsKey(pname))
                        {
                            var func = _funcDictionary[pname];
                            pv = func(pv);
                        }
                        HttpContext.Current.Response.Write(pv + "\t");
                    }
                    HttpContext.Current.Response.Write("\n");
                }
            }
            HttpContext.Current.Response.End();
        }

        private string GetPropertyName(Expression<Func<TModel, object>> expr)
        {
            var rtn = "";
            if (expr.Body is UnaryExpression)
            {
                rtn = ((MemberExpression)((UnaryExpression)expr.Body).Operand).Member.Name;
            }
            else if (expr.Body is MemberExpression)
            {
                rtn = ((MemberExpression)expr.Body).Member.Name;
            }
            else if (expr.Body is ParameterExpression)
            {
                rtn = ((ParameterExpression)expr.Body).Type.Name;
            }
            return rtn;
        }
    }
}
